package personal.vivian.aop.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import personal.vivian.aop.annotation.Transform;
import personal.vivian.aop.annotation.TransformParam;
import personal.vivian.builder.TransformBuilder;
import personal.vivian.common.Primitives;
import personal.vivian.constants.OperationStatus;
import personal.vivian.logic.DataContext;
import personal.vivian.logic.DataManager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;

@Service
@Slf4j
public class TransformAOPProcessor {
    @Autowired
    DataManager dataManager;
    public Object aopAround(ProceedingJoinPoint pjp, Transform cached) throws Throwable{
        Method m = getMethod(pjp);
        Annotation[][] annotations =m.getParameterAnnotations();
        Object[] args = new Object[pjp.getArgs().length];
        for(int i=0;i<annotations.length;i++){
            Object val = pjp.getArgs()[i];
            args[i]=val;
            Annotation[] annotations1=annotations[i];
            Optional<Annotation> op = Arrays.stream(annotations1).filter(a->a.annotationType().equals(TransformParam.class)).findFirst();
            TransformParam param=op.isPresent()? (TransformParam) op.get() :null;
            if(param!=null){
                if(val!=null){
                    args[i]=process(val,pjp.getTarget(),param.operate());
                }
            }
        }
        Object result =pjp.proceed(args);
        if(result!=null){
            result = process(result,pjp.getTarget(),cached.returnStatus());
        }
        return result;
    }

    private Object process(Object val, Object target, OperationStatus operate) {
        Class<?> clazz = val.getClass();
        if(Primitives.isPrimitive(clazz)||String.class.isAssignableFrom(clazz)){
            return val;
        }
        switch (operate){
            case Out:
            case In:
                TransformBuilder.create(clazz,dataManager).buildTrans(dataManager);
                DataContext context = dataManager.getContext(target);
                dataManager.resovle(context,val,operate);
                return val;
        }
        return val;
    }

    private Method getMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        MethodSignature methodSignature=null;
        if(!(pjp.getSignature() instanceof MethodSignature)){
            throw new IllegalArgumentException("@Cache can be only used for method");
        }
        methodSignature = (MethodSignature) pjp.getSignature();
        Method method = pjp.getTarget().getClass().getMethod(methodSignature.getName(),methodSignature.getParameterTypes());
       /* try {
            Field methodInvocation = pjp.getClass().getDeclaredField("methodInvocation");
            methodInvocation.setAccessible(true);
            ReflectiveMethodInvocation o = (ReflectiveMethodInvocation)methodInvocation.get(pjp);
            Method method1 = o.getMethod();
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }*/

      /*  MethodSignature methodSignature1 = (MethodSignature)pjp.getSignature();
        Method method1 = methodSignature1.getMethod();*/
        return method;
    }
}
